package algorithm;

import exception.BadNodeReachedException;
import graph.Graph;
import graph.Node;
import graph.Node_System;

import java.util.ArrayList;

@SuppressWarnings("all")
public class Scheduler {

	ArrayList<Node> bad_nodes_system;
	ArrayList<Node> bad_nodes_scheduler;

	ArrayList<Node> reached_nodes_system;
	ArrayList<Node> reached_nodes_scheduler;
	
	ArrayList<Node> visited_nodes_system;
	ArrayList<Node> visited_nodes_scheduler;
	
	Graph graph;
	Attractors attractors;
	
	public Scheduler(Graph g, Attractors a){
		
		graph = g;
		attractors = a;
		
	}
	int step = 0;
	
	public ArrayList<ArrayList<Node>> run() throws BadNodeReachedException{
		
		if (attractors.bad_nodes_scheduler().size() == 0 || attractors.bad_nodes_system().size() == 0)
			attractors.run();
		
		bad_nodes_system = (ArrayList<Node>) attractors.bad_nodes_system().clone();
		bad_nodes_scheduler = (ArrayList<Node>) attractors.bad_nodes_scheduler().clone();
		
		reached_nodes_system = new ArrayList<Node>();
		reached_nodes_scheduler = new ArrayList<Node>();
		
		visited_nodes_system = new ArrayList<Node>();
		visited_nodes_scheduler = new ArrayList<Node>();
		
		ArrayList<Node> old_reached_system;
		ArrayList<Node> old_reached_scheduler;
		
		Node_System initial_state = graph.initial_state();
		reached_nodes_system.add(initial_state);
		if(!is_bad_node_sys(initial_state)){
			do{
				old_reached_system = (ArrayList<Node>) reached_nodes_system.clone();
				old_reached_scheduler = (ArrayList<Node>)reached_nodes_scheduler.clone();
				
				for(Node n_sys : reached_nodes_system){
					if(!is_visited_system(n_sys)){
						if(n_sys.failure()){
							throw new BadNodeReachedException();
						}
						for(Node succ : n_sys.successors()){
							if(!is_reached_sch(succ)){
								reached_nodes_scheduler.add(succ);
							}
						}
						visited_nodes_system.add(n_sys);
					}
				}
				
				for(Node n_sch : reached_nodes_scheduler){
					if(!is_visited_scheduler(n_sch)){
						if(n_sch.failure() || is_bad_node_sch(n_sch)){
							System.out.println(n_sch.toString());
							throw new BadNodeReachedException();
						}
						boolean no_valid_successors = true;
						for(Node succ : n_sch.successors()){
							if (!is_bad_node_sys(succ)){
								
								no_valid_successors = false;
								
								if(!is_reached_sys(succ))
									reached_nodes_system.add(succ);
								
							}
						}
						if(no_valid_successors){
							throw new BadNodeReachedException("Impossible to go further");
						}
						visited_nodes_scheduler.add(n_sch);
					}
				}
	
	//			print results
				step++;
				System.out.println(step);
				System.out.println(old_reached_system.size() + " " + reached_nodes_system.size() + " " + old_reached_scheduler.size() + " "+ reached_nodes_scheduler.size());
			
			}while((old_reached_system.size() != reached_nodes_system.size()) || (old_reached_scheduler.size() != reached_nodes_scheduler.size()));
			
		}
		ArrayList<ArrayList<Node>> result = new ArrayList<ArrayList<Node>>();
		result.add(reached_nodes_system);
		result.add(reached_nodes_scheduler);
		
		return result;
	}
	
	private boolean is_bad_node_sys(Node n){
		for (Node bn : bad_nodes_system){
			if(bn.equals(n)){
				return true;
			}
		}
		return false;
	}
	private boolean is_bad_node_sch(Node n){
		for (Node bn : bad_nodes_scheduler){
			if(bn.equals(n)){
				return true;
			}
		}
		return false;
	}
	
	private boolean is_reached_sys(Node n){
		for (Node rn : reached_nodes_system){
			if(rn.equals(n)){
				return true;
			}
		}
		return false;
	}
	
	private boolean is_reached_sch(Node n){
		for (Node rn : reached_nodes_scheduler){
			if(rn.equals(n)){
				return true;
			}
		}
		return false;
	}
	
	private boolean is_visited_system(Node n){
		for(Node vn : visited_nodes_system){
			if(vn.equals(n))
				return true;
		}
		return false;
	}
	
	private boolean is_visited_scheduler(Node n){
		for(Node vn : visited_nodes_scheduler){
			if(vn.equals(n))
				return true;
		}
		return false;
	}
}

